/**
 * Quote helpers implement quote ability for all dialects.
 * These are basic block of query building
 *
 * Its better to implement all dialect implementation together here. Which will allow
 * even abstract generator to use them by just specifying dialect type.
 *
 * Defining these helpers in each query dialect will leave
 * code in dual dependency of abstract <-> specific dialect
 */

'use strict';

const Utils = require('../../../../utils');

/**
 * list of reserved words in PostgreSQL 10
 * source: https://www.postgresql.org/docs/10/static/sql-keywords-appendix.html
 *
 * @private
 */
const postgresReservedWords = 'all,analyse,analyze,and,any,array,as,asc,asymmetric,authorization,binary,both,case,cast,check,collate,collation,column,concurrently,constraint,create,cross,current_catalog,current_date,current_role,current_schema,current_time,current_timestamp,current_user,default,deferrable,desc,distinct,do,else,end,except,false,fetch,for,foreign,freeze,from,full,grant,group,having,ilike,in,initially,inner,intersect,into,is,isnull,join,lateral,leading,left,like,limit,localtime,localtimestamp,natural,not,notnull,null,offset,on,only,or,order,outer,overlaps,placing,primary,references,returning,right,select,session_user,similar,some,symmetric,table,tablesample,then,to,trailing,true,union,unique,user,using,variadic,verbose,when,where,window,with'.split(',');

/**
 *
 * @param {string}  dialect         Dialect name
 * @param {string}  identifier      Identifier to quote
 * @param {object}  [options]
 * @param {boolean} [options.force=false]
 * @param {boolean} [options.quoteIdentifiers=true]
 *
 * @returns {string}
 * @private
 */
function quoteIdentifier(dialect, identifier, options) {
  if (identifier === '*') return identifier;

  options = Utils.defaults(options || {}, {
    force: false,
    quoteIdentifiers: true
  });

  switch (dialect) {
    case 'sqlite':
    case 'mariadb':
    case 'mysql':
      return Utils.addTicks(Utils.removeTicks(identifier, '`'), '`');

    case 'postgres':
      const rawIdentifier = Utils.removeTicks(identifier, '"');

      if (
        options.force !== true &&
        options.quoteIdentifiers === false &&
        !identifier.includes('.') &&
        !identifier.includes('->') &&
        !postgresReservedWords.includes(rawIdentifier.toLowerCase())
      ) {
        // In Postgres, if tables or attributes are created double-quoted,
        // they are also case sensitive. If they contain any uppercase
        // characters, they must always be double-quoted. This makes it
        // impossible to write queries in portable SQL if tables are created in
        // this way. Hence, we strip quotes if we don't want case sensitivity.
        return rawIdentifier;
      }
      return Utils.addTicks(rawIdentifier, '"');
    case 'mssql':
      return `[${identifier.replace(/[[\]']+/g, '')}]`;

    default:
      throw new Error(`Dialect "${dialect}" is not supported`);
  }
}
module.exports.quoteIdentifier = quoteIdentifier;

/**
 * Test if a give string is already quoted
 *
 * @param {string} identifier
 *
 * @returns {boolean}
 * @private
 */
function isIdentifierQuoted(identifier) {
  return /^\s*(?:([`"'])(?:(?!\1).|\1{2})*\1\.?)+\s*$/i.test(identifier);
}
module.exports.isIdentifierQuoted = isIdentifierQuoted;
